﻿//-----------------------------------------------------------------------------
// Torque X Game Engine
// Copyright © GarageGames.com, Inc.
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.GamerServices;
using GarageGames.Torque.Core;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.RenderManager;
using GarageGames.Torque.Materials;
using GarageGames.Torque.MathUtil;
using GarageGames.Torque.Util;
using GarageGames.Torque.Sim;
using GarageGames.Torque.Lighting;
using GarageGames.Torque.T3D;
using GarageGames.Torque.GFX;



namespace GarageGames.Torque.RenderManager
{
    public class AvatarRenderInstance : RenderInstance
    {
    }



    /// <summary>
    /// Render manager for rendering sky boxes and sun flares.
    /// </summary>
    class AvatarRenderManager2 : BaseRenderManager
    {
        #region Public methods

        public override void AddElement(RenderInstance instance)
        {
            base.AddElement(instance);
        }



        public override void Clear()
        {
            base.Clear();
        }



        public override void RenderZPass(SceneRenderState srs)
        {
        }



        public override void RenderOpaquePass(SceneRenderState srs)
        {
#if DEBUG
            Profiler.Instance.StartBlock(_renderProfileBlock);
#endif

            for (int i = 0; i < _elementList.Count; i++)
            {
                RenderInstance element = _elementList[i];
                Matrix mat = Matrix.Identity;
                Vector3 pos = srs.CameraPosition;
                mat.Translation = pos;
                element.ObjectTransform = mat;
            }

            base.RenderOpaquePass(srs);

            // turn on the depth buffer
            srs.Gfx.Device.RenderState.DepthBufferEnable = true;

            if (!srs.SceneGraph.DoZPass)
                srs.Gfx.Device.RenderState.DepthBufferWriteEnable = true;

#if DEBUG
            Profiler.Instance.EndBlock(_renderProfileBlock);
#endif
        }



        public override void RenderTranslucentPass(SceneRenderState srs)
        {
        }

        #endregion


        #region Private, protected, internal fields

#if DEBUG
        ProfilerCodeBlock _renderProfileBlock = new ProfilerCodeBlock("AvatarRenderManager.RenderOpaquePass");
#endif

        #endregion
    }



    public class Avatar : TorqueObject, ISceneObject3D, IAnimatedObject
    {

        #region Static methods, fields, constructors

        /// <summary>
        /// Access to the global sun object. This should be used to look up the light direction
        /// for things like casting shadows.
        /// </summary>
        static public Avatar Instance
        {
            get { return _instance; }
        }

        static Avatar _instance;

        #endregion


        #region Constructors

        public Avatar()
        {
            _instance = this;
        }

        #endregion


        #region Public properties, operators, constants, and enums

        /// <summary>
        /// The 3D scenegraph associated with this scene object.
        /// </summary>
        public BaseSceneGraph SceneGraph
        {
            get { return _sceneGraph; }
            set
            {
                if (_sceneGraph == value)
                    return;

                if (value != null && !(value is T3DSceneGraph))
                    return;

                if (_sceneGraph != null)
                    _sceneGraph.RemoveObject(this);

                _sceneGraph = value as T3DSceneGraph;

                if (_sceneGraph != null && IsRegistered)
                    _sceneGraph.AddObject(this);
            }
        }



        [XmlIgnore]
        public Matrix Transform
        {
            get { return Matrix.Identity; }
            set { }
        }



        public Box3F WorldBox
        {
            get { return new Box3F(-1e9f, -1e9f, -1e9f, 1e9f, 1e9f, 1e9f); }
        }



        /// <summary>
        /// Always true.
        /// </summary>
        public bool Visible
        {
            get { return true; }
        }



        [TorqueCloneIgnore]
        [XmlIgnore]
        public SceneContainerData SceneContainerData
        {
            get { return _sceneContainerData; }
            set { _sceneContainerData = value; }
        }



        /*
        /// <summary>
        /// The material to render the first sun flare with.
        /// </summary>
        public RenderMaterial Material1
        {
            get { return _material1; }
            set { _material1 = value; }
        }



        /// <summary>
        /// The material to render the second sun flare with.
        /// </summary>
        public RenderMaterial Material2
        {
            get { return _material2; }
            set { _material2 = value; }
        }
        */


        /// <summary>
        /// The size of the sun.
        /// </summary>
        public float Size
        {
            get { return _size; }
            set { _size = value; }
        }


        /*
        /// <summary>
        /// The speed, in degrees per second, that the first sun flare rotates at.
        /// </summary>
        public float RotationSpeed1
        {
            get { return MathHelper.ToDegrees(_rotationSpeed1); }
            set { _rotationSpeed1 = MathHelper.ToRadians(value); }
        }



        /// <summary>
        /// The speed, in degrees per second, that the second sun flare rotates at.
        /// </summary>
        public float RotationSpeed2
        {
            get { return MathHelper.ToDegrees(_rotationSpeed2); }
            set { _rotationSpeed2 = MathHelper.ToRadians(value); }
        }
        


        /// <summary>
        /// The scale of the first sun. This is multiplied by Size to determine the actual size of the sun.
        /// </summary>
        public float Scale1
        {
            get { return _scale1; }
            set { _scale1 = value; }
        }



        /// <summary>
        /// The scale of the second sun. This is multiplied by Size to determine the actual size of the sun.
        /// </summary>
        public float Scale2
        {
            get { return _scale2; }
            set { _scale2 = value; }
        }



        /// <summary>
        /// The direction of the light. When the sun is registered, it attempts to determine this by looking
        /// for a directional light on a LightComponent that is attached to it. It can also be set directly
        /// here.
        /// </summary>
        public Vector3 LightDirection
        {
            get { return _direction; }
            set
            {
                _direction = value;
                _transform = MatrixUtil.CreateAimedTransform(Vector3.Zero, -_direction);
            }
        }
        */

        #endregion


        #region Public methods

        public void Render(SceneRenderState srs)
        {
            // push our transform
            srs.World.Push();
            _transform.Translation = srs.CameraPosition;
            srs.World.LoadMatrix(_transform);

            _RenderAvatar(srs);

            // pop our transform
            srs.World.Pop();
        }



        public void UpdateAnimation(float dt)
        {
            if (avatarRender != null && avatarRender.IsLoaded)
            {
                //update the animation
                avatarAnimation.Update(new TimeSpan((long)dt), true);
            }
        }



        public override bool OnRegister()
        {
            if (!base.OnRegister())
                return false;

            _SetupAvatar();

            ProcessList.Instance.AddAnimationCallback(this);

            // try to make sure we have a scenegraph
            if (_sceneGraph == null)
                _sceneGraph = TorqueObjectDatabase.Instance.FindObject<T3DSceneGraph>();

            // try to add us to our scenegraph
            if (_sceneGraph != null)
                _sceneGraph.AddObject(this);

            return true;
        }



        public override void OnUnregister()
        {
            base.OnUnregister();

            // try to remove us from our scenegraph
            if (_sceneGraph != null)
                _sceneGraph.RemoveObject(this);
        }

        #endregion


        #region Private, protected, internal methods

        protected void _SetupAvatar()
        {
            //create the avatar body
            avatarDescription = AvatarDescription.CreateRandom(AvatarBodyType.Male);
            //avatarDescription = Gamer.SignedInGamers[0].Avatar;


            avatarExpression = new AvatarExpression();
            avatarRender = new AvatarRenderer(avatarDescription, true);
            avatarAnimation = new AvatarAnimation(AvatarAnimationPreset.Clap);


            // Load 4 of the preset animations
            avatarAnimations = new AvatarAnimation[4];
            avatarAnimations[0] = new AvatarAnimation(AvatarAnimationPreset.Stand0);
            avatarAnimations[1] = new AvatarAnimation(AvatarAnimationPreset.Celebrate);
            avatarAnimations[2] = new AvatarAnimation(AvatarAnimationPreset.Clap);
            avatarAnimations[3] = new AvatarAnimation(AvatarAnimationPreset.Wave);

            /*
            //render the avatar in front of the camera
            avatarRender.View =
                Matrix.CreateLookAt(new Vector3(0, 1, 5), new Vector3(0, 1, 0),
                Vector3.Up);

            //spin the avatar to face the camera
            //            avatarRender.World = Matrix.CreateRotationY(MathHelper.ToRadians(180.0f));
            avatarRender.World = Matrix.CreateRotationY(MathHelper.Pi);

            //setup the avatar's projection matrix
//            FreeCameraComponent cam = TorqueObjectDatabase.Instance.FindObject<TorqueObject>("Player").Components.FindComponent<FreeCameraComponent>();
//            avatarRender.Projection = Matrix.CreatePerspectiveFieldOfView(cam.FOV, GFXDevice.Instance.Device.Viewport.AspectRatio, 0.01f, cam.FarDistance);
            */
        }



        void _RenderAvatar(SceneRenderState srs)
        {
            if (avatarRender != null && avatarRender.IsLoaded)
            {
                /*
                RenderInstance rInst = SceneRenderer.RenderManager.AllocateInstance<AvatarRenderInstance>();
                rInst.Type = RenderInstance.RenderInstanceType.Avatar;

                
                // geometry
                rInst.PrimitiveType = PrimitiveType.TriangleList;

                // vertices
                rInst.VertexSize = VertexPositionColorTexture.SizeInBytes;
                rInst.VertexDeclaration = srs.Gfx.GetVertexDeclarationVPCT();
                rInst.VertexBuffer = _vb.Instance;
                rInst.BaseVertex = 0;
                rInst.VertexCount = 4;

                // indices
                rInst.IndexBuffer = _ib.Instance;
                rInst.StartIndex = 0;
                rInst.PrimitiveCount = 2;

                // transform
                rInst.ObjectTransform = _animation1 * srs.World.Top;

                // material
                rInst.UTextureAddressMode = TextureAddressMode.Wrap;
                rInst.VTextureAddressMode = TextureAddressMode.Wrap;
                rInst.Material = _material1;

                SceneRenderer.RenderManager.AddInstance(rInst);

                RenderInstance rInst2 = SceneRenderer.RenderManager.AllocateInstance<SunRenderInstance>();
                rInst2.Type = RenderInstance.RenderInstanceType.Sky;

                // geometry
                rInst2.PrimitiveType = PrimitiveType.TriangleList;

                // vertices
                rInst2.VertexSize = VertexPositionColorTexture.SizeInBytes;
                rInst2.VertexDeclaration = srs.Gfx.GetVertexDeclarationVPCT();
                rInst2.VertexBuffer = _vb.Instance;
                rInst2.BaseVertex = 0;
                rInst2.VertexCount = 4;

                // indices
                rInst2.IndexBuffer = _ib.Instance;
                rInst2.StartIndex = 0;
                rInst2.PrimitiveCount = 2;

                // transform
                rInst2.ObjectTransform = _animation2 * srs.World.Top;

                // material
                rInst2.UTextureAddressMode = TextureAddressMode.Wrap;
                rInst2.VTextureAddressMode = TextureAddressMode.Wrap;
                rInst2.Material = _material2;

                SceneRenderer.RenderManager.AddInstance(rInst2);
                */


                /*
                                //avatarRender.View = srs.View;
                                avatarRender.View = Matrix.CreateTranslation(new Vector3(337, -171, 259));
                                avatarRender.World = srs.World.Top;
                                avatarRender.Projection = srs.Projection;
                */




                //render the avatar in front of the camera

                avatarRender.View =
                    Matrix.CreateLookAt(new Vector3(0, 1, 5), new Vector3(0, 1, 0),
                    Vector3.Up);


                //spin the avatar to face the camera
                avatarRender.World = Matrix.CreateRotationY(MathHelper.Pi);

                FreeCameraComponent cam = TorqueObjectDatabase.Instance.FindObject<TorqueObject>("Player").Components.FindComponent<FreeCameraComponent>();
                avatarRender.Projection = Matrix.CreatePerspectiveFieldOfView(cam.FOV, GFXDevice.Instance.Device.Viewport.AspectRatio, 0.01f, cam.FarDistance);


                /*
                Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
                Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
                unrotatedView;
                */

                //                sceneRenderState.World.Push();
                //                sceneRenderState.World.MultiplyMatrixLocal(SceneGroup.Transform3D.WorldMatrix);

                avatarRender.Draw(avatarAnimation.BoneTransforms, avatarExpression);

                //                base.Render(sceneRenderState);
                //
                //                sceneRenderState.World.Pop();
            }

        }

        #endregion


        #region Private, protected, internal fields

        AvatarExpression avatarExpression;
        AvatarDescription avatarDescription;
        AvatarRenderer avatarRender;
        AvatarAnimation avatarAnimation;
        AvatarAnimation[] avatarAnimations;

        T3DSceneGraph _sceneGraph;

        //Commented out to remove warnings.
        //Are currently unimplemented
        //RenderMaterial _material1;
        //RenderMaterial _material2;

        //Resource<VertexBuffer> _vb;
        //Resource<IndexBuffer> _ib;

        SceneContainerData _sceneContainerData = new SceneContainerData();

        Matrix _transform;
        Vector3 _direction = -Vector3.UnitZ;

        Matrix _animation1 = Matrix.Identity;
        Matrix _animation2 = Matrix.Identity;

        float _size = 10.0f;

        //Commented out to remove warnings.
        //Are currently unimplemented
        //float _rotationSpeed1 = 0.1745f;
        //float _rotationSpeed2 = -0.1745f;

        //float _rotation1;
        //float _rotation2;
        //float _scale1 = 0.8f;
        //float _scale2 = 1.0f;

        #endregion
    }
}
